package ClientManager;

// import ClientManager.ClientManager;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
public class MasterSocketManager {

    private Socket socket = null;
    private BufferedReader input = null;
    private PrintWriter output = null;
    private boolean isRunning = false;
    private Thread infoListener;

    private ClientManager clientManager;

    // 服务器的IP和端口号
    private final String master = "10.193.250.18";
    private final int PORT = 12345;

    // 使用map来存储需要处理的表名-sql语句的对应关系
    Map<String, String> commandMap = new HashMap<>();

    public MasterSocketManager(ClientManager clientManager) throws IOException {
        socket = new Socket(master, PORT);
        input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        output = new PrintWriter(socket.getOutputStream(), true);
        isRunning = true;

        this.clientManager = clientManager;

        this.sendMaster(10,"");

        this.listenToMaster(); // 开启监听线程
    }

    /**
     * 向master发送消息
     * @param type 消息类型
     * @param msg 消息内容
     */
    public void sendMaster(int type, String msg){
        output.println("<C2M>["+type+"]"+msg);
    }

    //接收来自master的消息并输出
    public void receiveFromMaster() throws IOException, InterruptedException {
        String line = null;
        if (socket.isClosed() || socket.isInputShutdown() || socket.isOutputShutdown()) {
            System.out.println("Socket closed!");
        } else {
            line = input.readLine();
        }
        if (line != null) {
            System.out.println("Receive from Master: "+line);
            processCmd(line);
        }

    }
    //处理master传来的命令,消息类型有更新节点，sql前获取表的region的ip和port，创建表时获得所在region的ip和port
    public void processCmd(String cmd){
        String parsed = cmd.substring(8);
        //返回sql的表名，ip，port
        if(cmd.startsWith("<M2C>[1]") || cmd.startsWith("<M2C>[2]")){
            //获取表名,ip地址+端口
            String[] params = parsed.split(",");
            int ipNums = params.length;
            if(ipNums == 1){//当前表没有region可以请求，直接退出
                System.out.println("There is no Region connecting to "+params[0]);
                return;
            }
            String tableName = params[0];
            String ip1 = params[1];
            this.clientManager.cacheManager.setMainCache(tableName,ip1);
            if(ipNums > 2){
                String ip2 = params[2];
                this.clientManager.cacheManager.setSubCache1(tableName,ip2);
            }
            if(ipNums > 3){
                String ip3 = params[3];
                this.clientManager.cacheManager.setSubCache2(tableName,ip3);
            }
        }else if(cmd.startsWith("<M2C>[7]")){//返回原来的和新的ip和port
            String[] params = parsed.split(",");
            String tableName = params[0];
            String oldIP = params[1];
            String newIP = params[2];
            //寻找cache中oldIP所在的map并替换为新的
            String result = this.clientManager.cacheManager.updateIP(tableName,oldIP,newIP);
            System.out.println(result);
            sendMaster(7,result);//返回给master结果
        }else if(cmd.startsWith("<M2C>[3]")){
            String[] tables = parsed.split(",");
            System.out.println("Tables:");
            for(String s : tables){
                System.out.print(s+", ");
            }
        }
    }

    // 开启一个监听线程
    public void listenToMaster() {
        infoListener = new InfoListener();
        infoListener.start();
    }

    public void process(String sql, String table) {
        this.commandMap.put(table, sql);
        // 用查询表对应的region ip,使用类型为1的协议
        this.sendMaster(1,table);
    }

    public void processCreate(String sql, String table) {
        this.commandMap.put(table, sql);
        // 创建表，使用类型为2的协议
        this.sendMaster(2,table + "," + sql);
    }

    // 关闭socket的方法，在输入quit的时候直接调用
    public void closeMasterSocket() throws IOException {
        socket.shutdownInput();
        socket.shutdownOutput();
        socket.close();
        infoListener.stop();
    }


    // 用一个内部类来实现客户端的监听
    class InfoListener extends Thread {
        @Override
        public void run() {
            System.out.println("Listening from master...");
            while (isRunning) {
                if (socket.isClosed() || socket.isInputShutdown() || socket.isOutputShutdown()) {
                    isRunning = false;
                    break;
                }

                try {
                    receiveFromMaster();
                } catch (IOException | InterruptedException e) {
                    e.printStackTrace();
                }

                try {
                    sleep(100);
                } catch (InterruptedException | NullPointerException e) {
                    e.printStackTrace();
                }
            }
        }
    }


}
